﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml.Linq;

namespace ListItemEditor
{
    public class ListDataProvider : IListDataProvider
    {
        private static Dictionary<string, int> _userIDToWebMap = new Dictionary<string, int>();

        public bool IsDocumentLibrary(GridViewDataSourceSettings settings)
        {
            bool isLibrary = false;
            Utility.OpenUseList(settings, (w, l) => { isLibrary = l is SPDocumentLibrary; }, true);
            return isLibrary;
        }

        public List<string> GetListFields(GridViewDataSourceSettings settings)
        {
            List<string> fields = null;
            Utility.OpenUseList(settings, (w, l) =>
            {
                fields = l.Fields.Cast<SPField>().
                    Select(f => f.InternalName).ToList();
            }, true);
            return fields;
        }

        public List<string> GetListViews(GridViewDataSourceSettings settings)
        {
            List<string> views = null;
            Utility.OpenUseList(settings, (w, l) => { views = l.Views.Cast<SPView>().Select(v => v.Title).ToList(); }, true);
            return views;
        }

        public string GetListUrl(GridViewDataSourceSettings settings)
        {
            string listUrl = string.Empty;
            Utility.OpenUseList(settings, (w, l) => { listUrl = w.Url.TrimEnd('/') + "/" + l.RootFolder.Url.TrimStart('/'); }, true);
            return listUrl;
        }

        public bool Explore(GridViewDataSourceSettings settings, ExploreType exploreType, DataRow row)
        {
            string url;
            switch (exploreType)
            {
                case ExploreType.Web:
                    url = settings.WebUrl; break;
                case ExploreType.Folder:
                    url = this.GetListUrl(settings);
                    if (!string.IsNullOrEmpty(settings.RootFolder))
                        url = url.TrimEnd('/') + "/" + Utility.GetFolderListRelativePath(url, settings.RootFolder).Trim('/');
                    break;
                default:
                    url = this.GetListUrl(settings); break;
            }
            return Utility.Explore(url, exploreType, row);
        }

        public CheckOutStatus GetCheckOutStatusFromDataRow(GridViewDataSourceSettings settings, DataRow row)
        {
            // the faster way of getting the checkout status
            if (!row.Table.Columns.Contains("CheckoutUser") || !row.Table.Columns.Contains("_Level")) return CheckOutStatus.None;
            string user = row["CheckoutUser"].ToString();
            if (string.IsNullOrEmpty(user)) return CheckOutStatus.None;

            int level;
            if (!int.TryParse(row["_Level"].ToString(), out level)) return CheckOutStatus.CheckedOutCurrentUser;
            return level == 255 ? CheckOutStatus.CheckedOutCurrentUser : CheckOutStatus.CheckedOutOtherUser;
        }

        public ModerationStatus GetModerationStatusFromDataRow(GridViewDataSourceSettings settings, DataRow row)
        {
            if (!row.Table.Columns.Contains("_ModerationStatus")) return ModerationStatus.Approved;
            string strVal = row["_ModerationStatus"].ToString();
            if (string.IsNullOrEmpty(strVal)) return ModerationStatus.Approved;

            int intVal;
            if (!int.TryParse(strVal, out intVal)) return ModerationStatus.Approved;
            if (intVal < 0 || intVal > 4) return ModerationStatus.Approved;

            return (ModerationStatus)intVal;
        }

        public List<SpecialFieldData> GetSpecialFieldData(GridViewDataSourceSettings settings, List<string> columnNames, bool fetchLookupData)
        {
            List<SpecialFieldData> datas = new List<SpecialFieldData>();
            Utility.OpenUseList(settings, delegate(SPWeb web, SPList list)
            {
                Dictionary<string, DataTable> tableMap = new Dictionary<string, DataTable>();
                foreach (string columnName in columnNames)
                {
                    if (Utility.UIReadonlyFields.Contains(columnName)) continue;

                    SpecialFieldData data = CheckIsLookupField(web, list, columnName, fetchLookupData, tableMap);
                    if (data != null) { datas.Add(data); continue; }

                    data = CheckIsLinkField(web, list, columnName);
                    if (data != null) { datas.Add(data); continue; }

                    data = CheckIsContentTypeField(web, list, columnName);
                    if (data != null) { datas.Add(data); continue; }
                }
            }, true);
            return datas;
        }

        public List<string> GetListNamesFromWeb(GridViewDataSourceSettings settings)
        {
            List<string> listNames = new List<string>();
            Utility.OpenUseWeb(settings.WebUrl,
                web =>
                {
                    listNames.AddRange(web.Lists.Cast<SPList>().Select(l => l.Title));
                }
            );
            return listNames;
        }

        public void ModifyView(GridViewDataSourceSettings settings, ViewData oldViewData, ViewData newViewData)
        {
            Utility.OpenUseList(settings, delegate(SPWeb web, SPList list)
            {
                SPView view = list.Views[oldViewData.Name];

                if (string.Compare(oldViewData.Name, newViewData.Name, false) != 0)
                {
                    view = view.Clone(newViewData.Name, view.RowLimit, view.Paged, false);
                }
                view.DefaultView = newViewData.Default;
                view.ViewFields.DeleteAll();
                foreach (string field in newViewData.ViewFields) view.ViewFields.Add(field);
                view.Query = newViewData.Query;
                view.Update();
            }, true);
        }

        public ViewData GetViewData(GridViewDataSourceSettings settings, string viewName)
        {
            ViewData viewData = null;
            Utility.OpenUseList(settings, delegate(SPWeb web, SPList list)
            {
                SPView view = list.Views[viewName];
                viewData = new ViewData()
                {
                    Default = view.DefaultView,
                    ID = view.ID,
                    Name = view.Title,
                    Query = view.Query,
                    ViewFields = view.ViewFields.Cast<string>().ToList(),
                    ViewUrl = web.Url.TrimEnd('/') + "/" + view.Url.TrimStart('/')
                };
            }, true);
            return viewData;
        }

        private SpecialFieldData CheckIsLinkField(SPWeb web, SPList list, string columnName)
        {
            string linkFieldName = list is SPDocumentLibrary ? "FileLeafRef" : "Title";
            if (columnName.Equals(linkFieldName)) return new SpecialFieldData() { FieldName = columnName, DataTable = null, DisplayFieldName = null, FieldDisplayType = FieldDisplayType.Link };
            return null;
        }

        private SpecialFieldData CheckIsContentTypeField(SPWeb web, SPList list, string columnName)
        {
            if ("ContentType".Equals(columnName))
            {
                DataTable tbl = new DataTable();
                tbl.Columns.Add("SID", typeof(string));
                foreach (SPContentType ctype in list.ContentTypes) tbl.Rows.Add(new object[] { ctype.Name });
                return new SpecialFieldData() { FieldName = columnName, DataTable = tbl, DisplayFieldName = "SID", FieldDisplayType = FieldDisplayType.DropDown };
            }
            return null;
        }

        private SpecialFieldData CheckIsLookupField(SPWeb web, SPList list, string columnName, bool fetchLookupData, Dictionary<string, DataTable> tableMap)
        {
            SPFieldLookup field = list.Fields.GetFieldByInternalName(columnName) as SPFieldLookup;
            if (field == null) return null;
            // TaxonomyField inherits SPFieldLookup but its handling should be different
            if (field.GetType().Name.Equals("TaxonomyField")) return null;

            string showField = this.GetSafeLookupFieldFromLookup(field);
            string lookupList = field.LookupList;
            Guid webID = field.LookupWebId;
            Guid listID = Guid.Empty;
            bool isUserField = field is SPFieldUser;

            if (!isUserField)
            {
                if (lookupList.IndexOf("-") < 0) return null;
                try { listID = new Guid(field.LookupList); } catch { return null; }
            }
            else if (string.IsNullOrEmpty(showField)) showField = "ImnName";
            
            FieldDisplayType displayType = field.AllowMultipleValues ? FieldDisplayType.MultiSelect : FieldDisplayType.DropDown;
            DataTable table = null;
            if (fetchLookupData)
            {
                string mapKey = webID + "%%" + lookupList + "%%" + showField;
                if (tableMap.ContainsKey(mapKey)) table = tableMap[mapKey];
                else
                {
                    if (isUserField) { table = GetLookupTable(web.SiteUserInfoList, showField); }
                    else
                    {
                        if (webID.Equals(web.ID)) table = GetLookupTable(web, listID, showField);
                        else
                        {
                            using (SPWeb lookupWeb = web.Site.OpenWeb(webID))
                            {
                                table = GetLookupTable(lookupWeb, listID, showField);
                            }
                        }
                    }
                    if (table != null)
                    {
                        table.Columns.Add(new DataColumn("SID", typeof(string)));
                        foreach (DataRow row in table.Rows) row["SID"] = row["ID"].ToString() + ";#" + row[showField].ToString();
                        tableMap[mapKey] = table;
                    }
                }
            }
            return new SpecialFieldData() { FieldName = columnName, DataTable = table, DisplayFieldName = showField, FieldDisplayType = displayType };
        }

        private string GetSafeLookupFieldFromLookup(SPFieldLookup lookup)
        {
            string lookupField = lookup.LookupField;
            if (string.IsNullOrEmpty(lookupField)) return lookupField;

            if (lookupField.IndexOf("$Resources:", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                XElement schema = XElement.Parse(lookup.SchemaXml);
                if (schema.Attribute("ShowField") != null) return schema.Attribute("ShowField").Value;
            }
            return lookupField;
        }

        private DataTable GetLookupTable(SPWeb web, Guid listID, string showFieldName)
        {
            SPList list = web.Lists[listID];
            return GetLookupTable(list, showFieldName);
        }

        private DataTable GetLookupTable(SPList list, string showFieldName)
        {
            SPQuery query = new SPQuery();
            query.ViewAttributes = "Scope=\"RecursiveAll\"";

            string viewFields = string.Format("<FieldRef Name='{0}' />", showFieldName);
            if (!"ID".Equals(showFieldName)) viewFields += "<FieldRef Name='ID' />";
            query.ViewFields = viewFields;
            query.Query = string.Format("<OrderBy><FieldRef Name='{0}' /></OrderBy>", showFieldName);

            DataTable table = list.GetItems(query).GetDataTable();
            return table;
        }
    }
}
